/*
 
 QUICKCYPHER - quickly encode messages using a substitution cypher

 The purpose of this program is to emulate the workings of a simple substitution 
 cypher. The cypher does not distinguish between "encrypted" or "decrypted" 
 states; rather it simply translates between the characters contained in the
 cypher and allows plain passthrough of any other remaining characters.

 Here's an example of a valid, albeit weak, cypher structure:

    TENIS
    -----
    POLAR

 When in action, this cypher will take a letter T and substitute it by P, E by
 O, N by L... etc. The opposite is also done: P becomes T, O becomes E... And
 finally, if no match within the cypher domain is found, the letter is passed
 on with no translation whatsoever (H would remain H, U remains U...)

 This cypher is valid because it contains two rows that are of the same length
 and contain distinct characters - no two equal characters appear in any row.

 Your cyphers may contain any character (alphanumeric, non-alphanumeric, 
 accented, extended Unicode...) depending on the complexity of the cypher you
 wish to have provided that character collisions do not happen.

    Copyright (C) 2015 Klaus Zimmermann 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 To report bugs, request features or other inquiries, please contact me 
 through my GNUSocial account @kzimmermann or visit my code repository at
 https://notabug.org/kzimmermann

 */

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "cypher.h"

bool validate_cypher(std::string row1, std::string row2)
{
    bool statuscode = true;
    for (auto letter1 : row1) {
        for (auto letter2 : row2) {
            if (letter1 == letter2) {
                std::cout << "Warning: cypher collision detected at '" << letter1 << "'!\n";
                statuscode = false;
            }
        }
    }
    return statuscode;
}

// Begin translation. Note that it's symmetrical, as in we don't exactly have
// a "state" saying encypted or decrypted. The cypher takes a letter, sees if
// it has it available and rotates it. That's it.

std::string translate(std::string input, std::string r1, std::string r2) {
    std::string output = ""; // this will become the final string.
    bool match = false;
    int row(0);
    int index(0);
        for (auto letter : input) {
            match = false; 
            for (auto letter1 = r1.begin(); 
                    letter1 != r1.end(); ++letter1) {
                if (letter == *letter1) {
                    // we got a match!
                    match = true;
                    row = 1;
                    index = letter1 - r1.begin();
                    break;
                }
            }
            if (match == false) {
                for (auto letter2 = r2.begin(); 
                        letter2 != r2.end(); ++letter2) {
                    if (letter == *letter2) {
                        // we got a match!
                        match = true;
                        row = 2;
                        index = letter2 - r2.begin();
                        break;
                    }
                }
            }
            // uncomment the following block for debugging!
            /*
            std::cout << "Now manipulating letter " << letter << ", match is " 
                << match << " row is " << row << " index is " << index << std::endl;
            */
            if (match) {
                std::string newletter;
                // get the letter in the opposite row at the same index:
                if (row == 1) {
                    newletter = r2.at(index);
                }
                else if (row == 2) {
                    newletter = r1.at(index);
                }
                // compose the new string by appending it to the string:
                output.append(newletter);
            }
            else {
                // push the character unchanged:
                std::string unchanged;
                unchanged = std::string(1, (char)letter);
                output.append(unchanged);
            }
        }
    return output;
}

// Instructions for use:
void helper()
{
    std::cout << "Translates text using cypher substitution." << std::endl;
    std::cout << "If no arguments are given, will enter 'interactive mode'\n";
    std::cout << "Otherwise pass a stream of data as it's input for translation.\n";
}

int main(int argc, char * argv[])
{
    std::string row1 = cypher::row1;
    std::string row2 = cypher::row2;
    if (!validate_cypher(row1, row2)) {
        std::cout << "You have a corrupted cypher.\n";
        std::cout << "Please fix it before running this program.\n";
        return 1;
    }

    // finalstring will be composed by appending samplestring to it:
    std::string samplestring;
    std::string finalstring;

    // decide if we're running as a script or in "interactive mode":
    if (isatty(STDIN_FILENO)) {
        // we're running in interactive mode.
        if (argc > 1 && (std::string(argv[1]) == "--help" 
                    || std::string(argv[1]) == "-h")) {
            helper();
            return 0;
        }
        std::cout << "Enter the text to be translated (terminate with EOF):\n>_ ";
        while (std::getline(std::cin, samplestring)) {
            finalstring.append(translate(samplestring, row1, row2));
            // newlines aren't appended, so we must add them manually:
            finalstring.append(std::string(1, (char)'\n'));
        }
        std::cout << finalstring << std::endl;
    }
    else {
        // read the content from stdin:
        while (std::getline(std::cin, samplestring)) {
            finalstring.append(translate(samplestring, row1, row2));
            // newlines aren't appended, so we must add them manually:
            finalstring.append(std::string(1, (char)'\n'));
        }
        std::cout << finalstring << std::endl;
    }
    return 0;
}

